﻿namespace HZY.Framework.Repository.EntityFramework.Interceptor;

/// <summary>
/// 审计 保存拦截器
/// </summary>
public abstract class AbstractAuditInterceptor : SaveChangesInterceptor
{
    /// <summary>
    /// SavingChanges
    /// </summary>
    /// <param name="eventData"></param>
    /// <param name="result"></param>
    /// <returns></returns>
    public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
    {
        SavingChanges(eventData);
        return base.SavingChanges(eventData, result);
    }

    /// <summary>
    /// SavingChangesAsync
    /// </summary>
    /// <param name="eventData"></param>
    /// <param name="result"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public override ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData,
        InterceptionResult<int> result, CancellationToken cancellationToken = default)
    {
        SavingChanges(eventData);
        return base.SavingChangesAsync(eventData, result, cancellationToken);
    }

    /// <summary>
    /// 获取当前用户
    /// </summary>
    /// <returns></returns>
    protected abstract string? GetCurrentUserId();

    /// <summary>
    /// 获取雪花 id
    /// </summary>
    /// <returns></returns>
    protected abstract long GetSnowflakeId();

    /// <summary>
    /// 添加 、 修改 、删除 拦截
    /// </summary>
    /// <param name="eventData"></param>
    protected virtual void SavingChanges(DbContextEventData eventData)
    {
        var userId = GetCurrentUserId();

        var entries = eventData.Context?.ChangeTracker.Entries();
        var entityEntries = entries as EntityEntry[] ?? entries?.ToArray();
        if (entityEntries is null) return;

        #region 处理 TableIdAttribute 标记的 id

        HandleTableId(entityEntries);

        #endregion

        #region Insert

        var insertEntries = entityEntries?
            .Where(w => w is { Entity: IEntity, State: EntityState.Added })
            .Select(item => item)
            .ToList();

        if (insertEntries is not null)
        {
            foreach (var item in insertEntries)
            {
                // 循环审计字段 集合
                foreach (var audit in RepositoryEntityFrameworkExtensions.AuditOptions)
                {
                    var timeFieldName = audit.CreationTimeFieldName;
                    var userIdFieldName = audit.CreatorUserIdFieldName;
                    this.HandleEntityEntry(item, timeFieldName, userIdFieldName, userId);
                }
            }
        }

        #endregion

        #region Update

        var updateEntries = entityEntries?
                .Where(w => w is { Entity: IEntity, State: EntityState.Modified })
                .Select(item => item)
                .ToList()
            ;

        if (updateEntries is not null)
        {
            foreach (var item in updateEntries)
            {
                // 循环审计字段 集合
                foreach (var audit in RepositoryEntityFrameworkExtensions.AuditOptions)
                {
                    var timeFieldName = audit.LastModificationTimeFieldName;
                    var userIdFieldName = audit.LastModifierUserIdFieldName;
                    this.HandleEntityEntry(item, timeFieldName, userIdFieldName, userId);
                }
            }
        }

        #endregion

        #region Delete

        var deleteEntries = entityEntries?
            .Where(w => w is { Entity: IEntity, State: EntityState.Deleted })
            .Select(item => item)
            .ToList();
        ;

        if (deleteEntries is not null)
        {
            foreach (var item in deleteEntries)
            {
                // 循环审计字段 集合
                foreach (var audit in RepositoryEntityFrameworkExtensions.AuditOptions)
                {
                    // 删除
                    var deleteFieldName = audit.IsDeletedFieldName;
                    // 删除对象
                    var deletePropertyEntry = item.Properties.FirstOrDefault(w => w.Metadata.Name == deleteFieldName);
                    if (deletePropertyEntry is null)
                    {
                        // 如果删除字段不存在 则跳过
                        continue;
                    }
                    else
                    {
                        deletePropertyEntry.CurrentValue = true;
                    }

                    var timeFieldName = audit.DeletionTimeFieldName;
                    var userIdFieldName = audit.DeleterUserIdFieldName;
                    this.HandleEntityEntry(item, timeFieldName, userIdFieldName, userId);
                }
            }
        }

        #endregion
    }

    /// <summary>
    /// 处理操作对象
    /// </summary>
    /// <param name="entityEntry"></param>
    /// <param name="timeFieldName"></param>
    /// <param name="userIdFieldName"></param>
    /// <param name="currentUserId"></param>
    protected virtual void HandleEntityEntry(EntityEntry entityEntry, string? timeFieldName, string? userIdFieldName, object? currentUserId)
    {
        // 查找对象是否包含某个字段
        var timePropertyEntry = entityEntry.Properties.FirstOrDefault(w => w.Metadata.Name == timeFieldName);
        if (timePropertyEntry != null)
        {
            if (entityEntry.State == EntityState.Added)
            {
                // 创建时间
                timePropertyEntry.CurrentValue = (DateTime?)timePropertyEntry.CurrentValue > DateTime.MinValue
                    ? timePropertyEntry.CurrentValue
                    : DateTime.Now;
            }
            else
            {
                timePropertyEntry.CurrentValue = DateTime.Now;
            }
        }

        // 创建人
        // 查找对象是否包含某个字段
        var userIdPropertyEntry = entityEntry.Properties.FirstOrDefault(w => w.Metadata.Name == userIdFieldName);
        if (userIdPropertyEntry == null) return;
        // userIdPropertyEntry
        if (userIdPropertyEntry.Metadata.ClrType == typeof(Guid) || userIdPropertyEntry.Metadata.ClrType == typeof(Guid?))
        {
            Guid.TryParse(currentUserId?.ToString(), out var guid);
            userIdPropertyEntry.CurrentValue = userIdPropertyEntry.CurrentValue ?? (guid == Guid.Empty ? null : guid);
        }
        else if (userIdPropertyEntry.Metadata.ClrType == typeof(long) || userIdPropertyEntry.Metadata.ClrType == typeof(long?))
        {
            long.TryParse(currentUserId?.ToString(), out var value);
            userIdPropertyEntry.CurrentValue = userIdPropertyEntry.CurrentValue ?? (value < 1 ? null : value);
        }
        else if (userIdPropertyEntry.Metadata.ClrType == typeof(int) || userIdPropertyEntry.Metadata.ClrType == typeof(int?))
        {
            int.TryParse(currentUserId?.ToString(), out var value);
            userIdPropertyEntry.CurrentValue = userIdPropertyEntry.CurrentValue ?? (value < 1 ? null : value);
        }
        else if (userIdPropertyEntry.Metadata.ClrType == typeof(string))
        {
            var value = currentUserId?.ToString();
            userIdPropertyEntry.CurrentValue = userIdPropertyEntry.CurrentValue ?? (string.IsNullOrWhiteSpace(value) ? null : value);
        }
        else
        {
            userIdPropertyEntry.CurrentValue = currentUserId;
        }
    }

    /// <summary>
    /// 处理 表 主键 id
    /// </summary>
    /// <param name="entityEntries"></param>
    protected virtual void HandleTableId(EntityEntry[] entityEntries)
    {
        var addEntries = entityEntries?
            .Where(w => w is { Entity: IEntity, State: EntityState.Added })
            .Select(item => item)
            .ToList()
        ;

        if (addEntries is null) return;

        foreach (var item in addEntries)
        {
            var propertyEntries = item.Properties
                .Where(w => w.Metadata.PropertyInfo?.GetCustomAttribute<TableIdAttribute>() is not null)
                .ToList();

            foreach (var propertyEntry in propertyEntries)
            {
                if (this.IsNullable(propertyEntry.Metadata.ClrType))
                {
                    if (propertyEntry.CurrentValue != null) continue;
                }

                var tableIdAttribute = propertyEntry.Metadata.PropertyInfo?.GetCustomAttribute<TableIdAttribute>();
                if (tableIdAttribute is null) continue;

                if (tableIdAttribute!.IdType == IdType.SnowflakeId)
                {
                    long.TryParse(propertyEntry.CurrentValue?.ToString(), out var result);
                    if (result > 0) continue;

                    // 判断  propertyEntry.CurrentValue 是否为 long 类型
                    if (propertyEntry.Metadata.ClrType == typeof(long) || propertyEntry.Metadata.ClrType == typeof(long?))
                    {
                        propertyEntry.CurrentValue = GetSnowflakeId();
                    }

                    if (propertyEntry.Metadata.ClrType == typeof(string))
                    {
                        propertyEntry.CurrentValue = GetSnowflakeId().ToString();
                    }

                    continue;
                }

                // uuid / guid
                if (tableIdAttribute!.IdType == IdType.UuId)
                {
                    if (propertyEntry.Metadata.ClrType == typeof(Guid) || propertyEntry.Metadata.ClrType == typeof(Guid?))
                    {
                        Guid.TryParse(propertyEntry.CurrentValue?.ToString(), out var guid);
                        if (guid != Guid.Empty) continue;
                    }

                    propertyEntry.CurrentValue = Guid.NewGuid();
                    continue;
                }

                // uuid string
                if (tableIdAttribute!.IdType != IdType.UuIdString) continue;
                propertyEntry.CurrentValue = Guid.NewGuid().ToString().Replace("-", "");
            }
        }
    }

    protected virtual bool IsNullable(Type type)
    {
        return Nullable.GetUnderlyingType(type) != null;
    }
}